//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2007
//
// 'Create POI by address' dialog.
//

# include "StdAfx.h"
# include "Resource.h"
# include "CreatePOIByAddress.h"
# include "Map.h"
# include "MapView.h"
# include "Position.h"
# include "PAHelpers.h"
# include "NearestRgnsList.h"
# include "TypeHelpers.h"
# include "CombinedAction.h"
# include "CreateObject.h"
# include "ChangeType.h"
# include "Rename.h"
# include "ChangeCity.h"
# include "ChangeZip.h"
# include "ChangeType.h"
# include "SetRgnExtra.h"
# include "Dialog.h"
# include "Button.h"
# include "Menu.h"
# include "PtAlgo.h"
# include "PrintUnits.h"
# include "Str.h"
# include "ParseHelpers.h"
# include "Globals.h"

static DWORD g_dwCancelTime;

void ShowAllStreetsInCity (HWND _hWnd, HWND _hwndButton, UINT _uiEditBox, const city_t * _pCity) {
	if (::GetTickCount () - g_dwCancelTime < 200)
		// NOTE: this click to arrow button is to close previous pop-up menu.
		return;

	CCritSec::CLock lock (g_cs);

	//
	// Prepare list of streets.
	//
	typedef std::set<string_t, LessString> streets_t;
	streets_t streets;

	float fD2Min = -1;
	for (size_t cKind = 0; cKind < rkMax; ++ cKind) {
		const layer_t & layer = g_map.layers [cKind];
		const rgns_t & rgns = layer.rgns;
		for (rgns_t::const_iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
			const rgn_t & rgn = * iRgn;
			if (rgn.IsDeleted () || rgn.IsWTR ())
				continue;

			if (! rgn.pExtra || rgn.pExtra->pCity != _pCity)
				continue;

			if (! rgn.pExtra->strStreetDesc.empty ())
				streets.insert (rgn.pExtra->strStreetDesc);
		}
	}
	if (streets.empty ())
		return;

	//
	// Prepare pop-up menu.
	//
	CPopupMenu pm;
	size_t cItem = 0;
	for (streets_t::const_iterator i = streets.begin (); i != streets.end (); ++ i) {
		pm.AppendItem (1 + cItem, i->c_str ());
		++ cItem;
	}
	const size_t cStreets = cItem;

	RECT rect;
	::GetWindowRect (_hwndButton, & rect);

	//
	// Show popup menu.
	//
	const int nCmd = pm.ShowAsPopup (_hWnd, rect);

	pa_item_t * pRet = NULL;
	if (nCmd > 0 && nCmd - 1 < (int) cStreets) {
		// Set the selected text to the desired edit box.
		char strText [256];
		::GetMenuString (pm, nCmd, strText, ARRAY_SIZE (strText), MF_BYCOMMAND);
		::SetDlgItemText (_hWnd, _uiEditBox, strText);
	} else
		g_dwCancelTime = ::GetTickCount ();
}

void ShowAllNumbersInStreet (HWND _hWnd, HWND _hwndButton, UINT _uiEditBox, const city_t * _pCity, const string_t & _strStreet) {
	if (::GetTickCount () - g_dwCancelTime < 200)
		// NOTE: this click to arrow button is to close previous pop-up menu.
		return;

	CCritSec::CLock lock (g_cs);

	//
	// Prepare list of streets.
	//
	typedef std::set<string_t, LessString> hnumbers_t;
	hnumbers_t numbers;

	float fD2Min = -1;
	for (size_t cKind = 0; cKind < rkMax; ++ cKind) {
		const layer_t & layer = g_map.layers [cKind];
		const rgns_t & rgns = layer.rgns;
		for (rgns_t::const_iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
			const rgn_t & rgn = * iRgn;
			if (rgn.IsDeleted () || rgn.IsWTR ())
				continue;

			if (! rgn.pExtra || rgn.pExtra->pCity != _pCity || ! (rgn.pExtra->strStreetDesc == _strStreet))
				continue;

			if (! rgn.pExtra->strNumber.empty ())
				numbers.insert (rgn.pExtra->strNumber);
		}
	}
	if (numbers.empty ())
		return;

	//
	// Prepare pop-up menu.
	//
	CPopupMenu pm;
	size_t cItem = 0;
	for (hnumbers_t::const_iterator i = numbers.begin (); i != numbers.end (); ++ i) {
		pm.AppendItem (1 + cItem, i->c_str ());
		++ cItem;
	}
	const size_t cStreets = cItem;

	RECT rect;
	::GetWindowRect (_hwndButton, & rect);

	//
	// Show popup menu.
	//
	const int nCmd = pm.ShowAsPopup (_hWnd, rect);

	pa_item_t * pRet = NULL;
	if (nCmd > 0 && nCmd - 1 < (int) cStreets) {
		// Set the selected text to the desired edit box.
		char strText [256];
		::GetMenuString (pm, nCmd, strText, ARRAY_SIZE (strText), MF_BYCOMMAND);
		::SetDlgItemText (_hWnd, _uiEditBox, strText);
	} else
		g_dwCancelTime = ::GetTickCount ();
}

//////////////////////////////////////////////////////////////////

bool ThereAreNumbersAlongRoads () {
	CCritSec::CLock lock (g_cs);

	const layer_t & layer = g_map.layers [rkPolyline];
	const rgns_t & rgns = layer.rgns;
	for (rgns_t::const_iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
		const rgn_t & rgn = * iRgn;
		if (rgn.IsDeleted () || rgn.IsWTR ())
			continue;

		const size_t cElements = rgn.elements.size ();
		for (size_t cElement = 0; cElement < cElements; ++ cElement) {
			const rgn_element_t & element = rgn.elements [cElement];
			if (element.bDeleted || ! element.pExtra)
				continue;

			const point_extras_t & PointExtras = element.pExtra->PointExtras;
			const size_t cPoints = PointExtras.size ();

			for (size_t cPoint = 0; cPoint < cPoints; ++ cPoint) {
				const numbers_t & Numbers = PointExtras [cPoint].Numbers;
				if (Numbers.bHasNumbers)
					return true;
			}
		}
	}

	return false;
}

static
bool _IsInRange (unsigned long _ul, unsigned long _ul1, unsigned long _ul2) {
	if (_ul1 < _ul2)
		return _ul1 <= _ul && _ul <= _ul2;
	else
		return _ul2 <= _ul && _ul <= _ul1;
}

static
void _GetMiddlePoint (
	unsigned long _ul, unsigned long _ul1, unsigned long _ul2,
	const points_t & _points, size_t _cPoint1, size_t _cPoint2,
	point_t & _point
) {
	assert (_cPoint1 < _points.size ());
	assert (_cPoint2 < _points.size ());
	assert (_cPoint1 < _cPoint2);

	const fCosY = ::cos (_points [0].y*c_PI/180);

	// Measure full length.
	float L = 0;
	size_t c;
	for (c = _cPoint1; c < _cPoint2; ++ c)
		L += ::hypot ((_points [c + 1].x - _points [c].x)*fCosY, _points [c + 1].y - _points [c].y);
	const float l = L/(::labs (_ul1 - _ul2) + 1);

	// Find middle segment.
	float M = _ul1 < _ul2 ? l/2 + (_ul - _ul1)*l : L - l/2 - (_ul - _ul2)*l;
	for (c = _cPoint1; c < _cPoint2; ++ c) {
		const float fDL = ::hypot ((_points [c + 1].x - _points [c].x)*fCosY, _points [c + 1].y - _points [c].y);
		if (M > fDL)
			M -= fDL;
		else {
			_point.x = _points [c].x + (_points [c + 1].x - _points [c].x)*M/fDL;
			_point.y = _points [c].y + (_points [c + 1].y - _points [c].y)*M/fDL;
			return;
		}
	}
}

static
bool _FindPositionByAddress (const city_t * _pCity, const string_t & _strStreet, const string_t & _strNumber, point_t & _point) {
	CCritSec::CLock lock (g_cs);

	// Get numeric value of the Number.
	unsigned long ulNumber = 0;
	bool bNumberIsNumeric = false;
	const char * const pNumberEnd = _strNumber.c_str () + _strNumber.size ();
	const char * p = NULL;
	if (ParseDecU (_strNumber.c_str (), pNumberEnd, & p, ulNumber))
		bNumberIsNumeric = p == pNumberEnd;
	const bool bNumberIsOdd = (ulNumber & 0x1) != 0;

	for (int nKind = rkPolygon; nKind >= rkPoint; -- nKind) {
		const layer_t & layer = g_map.layers [nKind];
		const rgns_t & rgns = layer.rgns;
		for (rgns_t::const_iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
			const rgn_t & rgn = * iRgn;
			if (rgn.IsDeleted () || rgn.IsWTR ())
				continue;

			if (rgn.pExtra) {
				if (rgn.pExtra->pCity == _pCity && rgn.pExtra->strStreetDesc == _strStreet && rgn.pExtra->strNumber == _strNumber) {
					_point.x = rgn.rectBound.MeanX ();
					_point.y = rgn.rectBound.MeanY ();
					return true;
				}
			}

			if (nKind == rkPolyline && bNumberIsNumeric) {
				if (! (rgn.strLabel == _strStreet))
					continue;

				const city_t * const pCityOfRgn = rgn.pExtra ? rgn.pExtra->pCity : NULL;

				const size_t cElements = rgn.elements.size ();
				for (size_t cElement = 0; cElement < cElements; ++ cElement) {
					const rgn_element_t & element = rgn.elements [cElement];
					if (element.bDeleted || ! element.pExtra)
						continue;
					const points_t & points = element.points;

					const point_extras_t & PointExtras = element.pExtra->PointExtras;
					const size_t cPoints = __min (PointExtras.size (), points.size ());

					const numbers_t * pNumbersPrev = NULL;
					size_t cPointPrev = -1;
					for (size_t cPoint = 0; cPoint < cPoints; ++ cPoint) {
						const numbers_t & Numbers = PointExtras [cPoint].Numbers;
						if (! Numbers.bHasNumbers)
							continue;

						if (pNumbersPrev != NULL) {
							// Check left side.
							if (pNumbersPrev->wLeftParity != 0 && _IsInRange (ulNumber, pNumbersPrev->wLeftAfter, Numbers.wLeftBefore)) {
								if (
									(pNumbersPrev->wLeftParity == 1 && ! bNumberIsOdd) ||
									(pNumbersPrev->wLeftParity == 2 &&   bNumberIsOdd) ||
									pNumbersPrev->wLeftParity == 3
								) {
									const city_t * const pCity = pNumbersPrev->pCityLeftAfter ? pNumbersPrev->pCityLeftAfter : pCityOfRgn;
									if (pCity == _pCity) {
										_GetMiddlePoint (ulNumber, pNumbersPrev->wLeftAfter, Numbers.wLeftBefore, points, cPointPrev, cPoint, _point);
										return true;
									}
								}
							}

							// Check right side.
							if (pNumbersPrev->wRightParity != 0 && _IsInRange (ulNumber, pNumbersPrev->wRightAfter, Numbers.wRightBefore)) {
								if (
									(pNumbersPrev->wRightParity == 1 && ! bNumberIsOdd) ||
									(pNumbersPrev->wRightParity == 2 &&   bNumberIsOdd) ||
									pNumbersPrev->wRightParity == 3
								) {
									const city_t * const pCity = pNumbersPrev->pCityRightAfter ? pNumbersPrev->pCityRightAfter : pCityOfRgn;
									if (pCity == _pCity) {
										_GetMiddlePoint (ulNumber, pNumbersPrev->wRightAfter, Numbers.wRightBefore, points, cPointPrev, cPoint, _point);
										return true;
									}
								}
							}
						}

						pNumbersPrev = & Numbers;
						cPointPrev = cPoint;
					}
				}
			}
		}
	}

	return false;
}

////////////////////////////////////////////////////////////////

class CCreatePOIByAddress : public CDialog {
  public:
	CCreatePOIByAddress () :
		CDialog (IDD_CREATE_POI_BY_ADDRESS),
		m_ushType (0),
		m_bTypeDefined (false),
		m_pCity (NULL),
		m_pZip (NULL),
		m_bPtDefined (false),
		m_bPtPrevDefined (false)
	{}

	bool OnInit (HWND _hwndFocus) {
		// Number.
		SetButtonIcon (m_hWnd, IDC_NEAREST_NUMBER, IDI_DOWN);

		// Street.
		SetAutoCompleteForStreet (GetDlgItem (IDC_STREET), NULL);
		SetButtonIcon (m_hWnd, IDC_NEAREST_STREET, IDI_DOWN);

		// City.
		if (g_map.Cities.empty ()) {
			EnableDlgItem (IDC_CITY,        false);
			EnableDlgItem (IDC_SELECT_CITY, false);
			EnableDlgItem (IDC_REMOVE_CITY, false);
		}
		SetButtonIcon (m_hWnd, IDC_REMOVE_CITY,    IDI_CROSS);
		SetButtonIcon (m_hWnd, IDC_NEAREST_CITY,   IDI_DOWN);

		// Zip code.
		if (g_map.Zips.empty ()) {
			EnableDlgItem (IDC_ZIP,        false);
			EnableDlgItem (IDC_SELECT_ZIP, false);
			EnableDlgItem (IDC_REMOVE_ZIP, false);
		}
		if (g_map.Zips.empty () || ! ThereAreNearestRgns (nrZip))
			EnableDlgItem (IDC_NEAREST_ZIP, false);
		SetButtonIcon (m_hWnd, IDC_REMOVE_ZIP,     IDI_CROSS);
		SetButtonIcon (m_hWnd, IDC_NEAREST_ZIP,    IDI_DOWN);

		// Position.
		SetButtonIcon (m_hWnd, IDC_LOCATE,         IDI_LOCATE);

		// Contact.
		SetButtonIcon (m_hWnd, IDC_REMOVE_PHONE,   IDI_CROSS);
		SetButtonIcon (m_hWnd, IDC_REMOVE_FAX,     IDI_CROSS);
		SetButtonIcon (m_hWnd, IDC_REMOVE_EMAIL,   IDI_CROSS);
		SetButtonIcon (m_hWnd, IDC_REMOVE_WEBPAGE, IDI_CROSS);
		SetButtonIcon (m_hWnd, IDC_NEAREST_PHONE,  IDI_DOWN);
		SetButtonIcon (m_hWnd, IDC_NEAREST_FAX,    IDI_DOWN);

		_EnableButtons ();

		return true;
	}

	// Override.
	virtual void OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
		switch (_wCmd) {
			case IDC_LABEL:
			case IDC_NUMBER:
			case IDC_STREET:
			case IDC_WEBPAGE:
			case IDC_POSITION:
				if (_wNotifyCode == EN_UPDATE)
					_EnableButtons ();
				break;

			case IDC_CHANGE_TYPE:
				if (ShowChangeType (m_hWnd, rkPoint, m_ushType)) {
					FillType (m_hWnd, IDC_TYPE, rkPoint, m_ushType);
					m_bTypeDefined = true;
					_EnableButtons ();
				}
				break;

			case IDC_SELECT_CITY:
				if (SelectCity (m_hWnd, true, m_pCity)) {
					FillCity (m_hWnd, IDC_CITY, m_pCity);
					_EnableButtons ();
				}
				break;
			case IDC_REMOVE_CITY:
				m_pCity = NULL;
				FillCity (m_hWnd, IDC_CITY, NULL);
				_EnableButtons ();
				break;

			case IDC_SELECT_ZIP:
				if (ShowChangeZip (m_hWnd, m_pZip)) {
					FillZip (m_hWnd, IDC_ZIP, m_pZip);
					_EnableButtons ();
				}
				break;
			case IDC_REMOVE_ZIP:
				m_pZip = NULL;
				FillZip (m_hWnd, IDC_ZIP, NULL);
				_EnableButtons ();
				break;

			case IDC_NEAREST_NUMBER: {
				string_t strStreet;
				GetDlgItemText (IDC_STREET, strStreet);
				if (m_pCity == NULL || strStreet.empty ()) {
					rect_t rectVisible;
					GetVisibleRect (rectVisible);

					ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_NUMBER, nrNumber, point_t (rectVisible.MeanX (), rectVisible.MeanY ())); break;
				} else {
					ShowAllNumbersInStreet (m_hWnd, _hwndCtl, IDC_NUMBER, m_pCity, strStreet);
				}
				break;
			}

			case IDC_NEAREST_STREET: {
				if (m_pCity == NULL) {
					rect_t rectVisible;
					GetVisibleRect (rectVisible);

					ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_STREET, nrStreet, point_t (rectVisible.MeanX (), rectVisible.MeanY ())); break;
				} else {
					ShowAllStreetsInCity (m_hWnd, _hwndCtl, IDC_STREET, m_pCity);
				}
				break;
			}

			case IDC_NEAREST_CITY: {
				rect_t rectVisible;
				GetVisibleRect (rectVisible);

				city_t * const pCity = static_cast<city_t *> (ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_CITY, nrCity, point_t (rectVisible.MeanX (), rectVisible.MeanY ())));
				if (pCity) {
					m_pCity = pCity;
					FillCity (m_hWnd, IDC_CITY, m_pCity);
					_EnableButtons ();
				}
				break;
			}

			case IDC_NEAREST_ZIP: {
				rect_t rectVisible;
				GetVisibleRect (rectVisible);

				zip_t * const pZip = static_cast<zip_t *> (ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_ZIP, nrZip, point_t (rectVisible.MeanX (), rectVisible.MeanY ())));
				if (pZip) {
					m_pZip = pZip;
					FillZip (m_hWnd, IDC_ZIP, m_pZip);
					_EnableButtons ();
				}
				break;
			}

			case IDC_NEAREST_PHONE: {
				rect_t rectVisible;
				GetVisibleRect (rectVisible);

				ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_PHONE, nrPhone, point_t (rectVisible.MeanX (), rectVisible.MeanY ()));
				break;
			}

			case IDC_NEAREST_FAX: {
				rect_t rectVisible;
				GetVisibleRect (rectVisible);

				ShowNearestRgnsList (m_hWnd, _hwndCtl, IDC_FAX,   nrPhone, point_t (rectVisible.MeanX (), rectVisible.MeanY ()));
				break;
			}

			case IDC_GO_WEB: {
				string_t strURL;
				GetDlgItemText (IDC_WEBPAGE, strURL);
				if (! strURL.empty ())
					::ShellExecute (m_hWnd, NULL, strURL.c_str (), NULL, NULL, SW_SHOWNORMAL);
				break;
			}

			case IDC_RESET_CONTACT:
				SetDlgItemText (IDC_PHONE,       NULL);
				SetDlgItemText (IDC_FAX,         NULL);
				SetDlgItemText (IDC_EMAIL,       NULL);
				SetDlgItemText (IDC_WEBPAGE,     NULL);
				SetDlgItemText (IDC_DESCRIPTION, NULL);
				break;

			case IDC_REMOVE_PHONE:   SetDlgItemText (IDC_PHONE,   NULL); break;
			case IDC_REMOVE_FAX:     SetDlgItemText (IDC_FAX,     NULL); break;
			case IDC_REMOVE_EMAIL:   SetDlgItemText (IDC_EMAIL,   NULL); break;
			case IDC_REMOVE_WEBPAGE: SetDlgItemText (IDC_WEBPAGE, NULL); break;

			case IDC_FIND: {
				string_t strStreet;
				GetDlgItemText (IDC_STREET, strStreet);

				string_t strNumber;
				GetDlgItemText (IDC_NUMBER, strNumber);

				if (_FindPositionByAddress (m_pCity, strStreet, strNumber, m_pt)) {
					char strPos [64];
					PrintPosition (strPos, m_pt);

					SetDlgItemText (IDC_POSITION, strPos);

					m_bPtDefined = true;
					_EnableButtons ();
				} else {
					SetDlgItemText (IDC_POSITION, NULL);

					m_bPtDefined = false;
					_EnableButtons ();
				}
				break;
			}

			case IDC_LOCATE:
				if (m_bPtDefined) {
					//SetScale (g_map.Levels (0).);
					SetToCenter (m_pt);
					Refresh ();
				}
				break;

			case IDC_CREATE: {
				if (! m_bPtDefined || ! m_bTypeDefined)
					break;

				const int nDepth = BeginCombinedAction ();

				// Create POI.
				points_t points (1);
				points [0] = m_pt;
				rgn_t & rgn = CreateObject (points, rkPoint, 0);

				// Set type.
				ChangeType (rgn, m_ushType);

				// Set label.
				string_t strLabel;
				GetDlgItemText (IDC_LABEL, strLabel);
				RenameObject (rgn, strLabel);

				// Set city.
				ChangeCity (rgn, m_pCity);

				// Set zip.
				ChangeZip (rgn, m_pZip);

				// Set street.
				string_t strStreet;
				GetDlgItemText (IDC_STREET, strStreet);
				SetObjectPAStreet (rgn, strStreet);

				// Set number.
				string_t strNumber;
				GetDlgItemText (IDC_NUMBER, strNumber);
				SetObjectPANumber (rgn, strNumber);

				// Set phone.
				string_t strPhone;
				GetDlgItemText (IDC_PHONE, strPhone);
				SetObjectPhone (rgn, strPhone);

				// Set fax.
				string_t strFax;
				GetDlgItemText (IDC_FAX, strFax);
				SetObjectFax (rgn, strFax);

				// Set e-mail.
				string_t strEmail;
				GetDlgItemText (IDC_EMAIL, strEmail);
				SetObjectEmail (rgn, strEmail);

				// Set URL.
				string_t strWebPage;
				GetDlgItemText (IDC_WEBPAGE, strWebPage);
				SetObjectWebPage (rgn, strWebPage);

				// Set description.
				string_t strDescription;
				GetDlgItemText (IDC_DESCRIPTION, strDescription);
				SetObjectDescription (rgn, strDescription);

				string_t strName = "create POI";
				if (! strLabel.empty ()) {
					strName.append (" \'");
					strName.append (strLabel);
					strName.append ("\'");
				}
				EndCombinedAction (nDepth, strName.c_str ());

				Refresh ();

				// Disable 'Create'.
				m_ushTypePrev = m_ushType;
				m_strLabelPrev = strLabel;
				m_ptPrev = m_pt;
				m_bPtPrevDefined = true;
				_EnableButtons ();

				// Reset position.
				SetDlgItemText (IDC_POSITION, NULL);
				m_bPtDefined = false;

				break;
			}

			case IDCANCEL:
				//EndDialog (0);
				Hide ();
				break;
		}
	}

//	virtual void OnDestroy () {
//		delete CCreatePOIByAddress::s_pCreatePOIByAddress;
//		CCreatePOIByAddress::s_pCreatePOIByAddress = NULL;
//	}


	static CCreatePOIByAddress * s_pCreatePOIByAddress;

  private:
	USHORT m_ushType;
	bool m_bTypeDefined;
	city_t * m_pCity;
	zip_t  * m_pZip;

	point_t m_pt;
	bool m_bPtDefined;

	point_t m_ptPrev;
	bool m_bPtPrevDefined;
	string_t m_strLabelPrev;
	USHORT m_ushTypePrev;

	void _EnableButtons ();
};

void CCreatePOIByAddress::_EnableButtons () {
	// Web page.
	string_t strURL;
	GetDlgItemText (IDC_WEBPAGE, strURL);
	EnableDlgItem (IDC_GO_WEB, ! strURL.empty ());

	// Position.
	string_t strStreet;
	GetDlgItemText (IDC_STREET, strStreet);
	string_t strNumber;
	GetDlgItemText (IDC_NUMBER, strNumber);
	EnableDlgItem (IDC_FIND,   m_pCity != NULL && ! strStreet.empty () && ! strNumber.empty ());
	EnableDlgItem (IDC_LOCATE, m_bPtDefined);

	// Create.
	string_t strLabel;
	GetDlgItemText (IDC_LABEL, strLabel);
	const bool bEqual = m_bPtDefined && m_bPtPrevDefined && m_ptPrev == m_pt && m_strLabelPrev == strLabel && m_ushTypePrev == m_ushType;
	EnableDlgItem (IDC_CREATE, ! bEqual && m_bPtDefined && m_bTypeDefined);
}

CCreatePOIByAddress * CCreatePOIByAddress::s_pCreatePOIByAddress;

void CreatePOIByAddress () {
	bool bNoAddressData = false;
	if (g_map.Cities.empty () || ! ThereAreNearestRgns (nrCity))
		bNoAddressData = true;
	else if (! ThereAreNumbersAlongRoads ()) {
		if (! ThereAreNearestRgns (nrStreet) || ! ThereAreNearestRgns (nrNumber))
			bNoAddressData = true;
	}
	if (bNoAddressData) {
		ErrorMsgBox ("The map has no address data.");
		return;
	}

	ShowModeless (CCreatePOIByAddress::s_pCreatePOIByAddress, g_hWnd);
}

# include "HoluxTypes.h"

bool CreatePOIByAddressIsAvailable () {
	return ! g_map.Cities.empty () && g_map.pTypeSet != & g_tsHolux;
}
